/*
 * symbol_table.c
 *
 *  Created on: Nov 25, 2010
 *      Author: cpy.prefers.you@gmail.com
 */
 
#include "headers.h"

Symbol_T *HashTable[HASH_TABLE_SIZE];

Symbol_T *NewSymbol()
{
    return (Symbol_T *)calloc(1, sizeof(Symbol_T));
}

int MemSize(int Type)
{
    switch(Type)
    {
        case TYPE_KEYWORD:
        case TYPE_IDENTIFIER:
            return TOKEN_LEN;
        case TYPE_STRING:
            return STRING_LEN;
        default:
            return TOKEN_LEN;
    }
    return 0;
}

/* actually the well-known ELFHash */
static int Hash(char *Name)
{
    unsigned long HashValue = 0;
    unsigned long Symbol;
    while (*Name)
    {
        HashValue <<= 4;
        HashValue += *Name++;
        Symbol = HashValue & 0xf0000000l;
        if (Symbol)
            HashValue ^= Symbol >> 24;
        HashValue &= ~Symbol;
    }   
    return HashValue % HASH_TABLE_SIZE;
}

Symbol_T *InsertSymbol(Symbol_T *SymbolToInsert)
{
    Symbol_T *Symbol = calloc(1, sizeof(Symbol_T));
    memcpy(Symbol, SymbolToInsert, sizeof(Symbol_T));
    Symbol->Name = malloc(MemSize(SymbolToInsert->Type));
    strcpy(Symbol->Name, SymbolToInsert->Name);

    int HashKey = Hash(Symbol->Name);

    if (HashTable[HashKey])
    {
        Symbol->Next = HashTable[HashKey]->Next;
        HashTable[HashKey]->Next = Symbol;
    }
    else
    {
        HashTable[HashKey] = Symbol;
        HashTable[HashKey]->Next = 0;
    }
    return Symbol;
}

void RemoveSymbol(char *Name)
{
    Symbol_T *Symbol = LookupName(Name);
    *Symbol = *Symbol->Next;
    return;
}

Symbol_T *LookupName(char *Name)
{
    int HashKey = Hash(Name);
    Symbol_T *Symbol = HashTable[HashKey];
    while (Symbol)
    {
        if (!strcmp(Name, Symbol->Name))
            break;
        Symbol = Symbol->Next;
    }
    return Symbol;
}

#define InsertKeyword(_Name, _Type)     InsertSymbol(&(Symbol_T){ .Name = _Name, .Type = TYPE_KEYWORD, .KeywordType = _Type })
#define InsertOperator(_Name, _Type)    InsertSymbol(&(Symbol_T){ .Name = _Name, .Type = _Type })

#ifdef  SYMBOL_INSERT_MODE

void InsertKeyword(char *Name, int KeywordType)
{
    Symbol_T *Symbol = calloc(1, sizeof(Symbol_T));
    Symbol->Name = malloc(TOKEN_LEN);
    strcpy(Symbol->Name, Name);
    Symbol->Type = TYPE_KEYWORD;
    Symbol->KeywordType = KeywordType;
    InsertSymbol(Symbol);
    return;
}

void InsertOperator(char *Name, int Type)
{
    Symbol_T *Symbol = calloc(1, sizeof(Symbol_T));
    Symbol->Name = malloc(TOKEN_LEN);
    strcpy(Symbol->Name, Name);
    Symbol->Type = Type;
    InsertSymbol(Symbol);
    return;
}

#endif

void SymbolTableInit()
{
    InsertKeyword("break",     KEYWORD_CONTROL_CONSTRUCT);
    InsertKeyword("case",      KEYWORD_CONTROL_CONSTRUCT);
    InsertKeyword("continue",  KEYWORD_CONTROL_CONSTRUCT);
    InsertKeyword("default",   KEYWORD_CONTROL_CONSTRUCT);
    InsertKeyword("do",        KEYWORD_CONTROL_CONSTRUCT);
    InsertKeyword("else",      KEYWORD_CONTROL_CONSTRUCT);
    InsertKeyword("for",       KEYWORD_CONTROL_CONSTRUCT);
    InsertKeyword("goto",      KEYWORD_CONTROL_CONSTRUCT);
    InsertKeyword("if",        KEYWORD_CONTROL_CONSTRUCT);
    InsertKeyword("switch",    KEYWORD_CONTROL_CONSTRUCT);
    InsertKeyword("while",     KEYWORD_CONTROL_CONSTRUCT);
    InsertKeyword("char",      KEYWORD_DATA_TYPE);
    InsertKeyword("double",    KEYWORD_DATA_TYPE);
    InsertKeyword("float",     KEYWORD_DATA_TYPE);
    InsertKeyword("int",       KEYWORD_DATA_TYPE);
    InsertKeyword("enum",      KEYWORD_DATA_TYPE);
    InsertKeyword("void",      KEYWORD_DATA_TYPE);
    InsertKeyword("long",      KEYWORD_MODIFIER_LEN);
    InsertKeyword("short",     KEYWORD_MODIFIER_LEN);
    InsertKeyword("signed",    KEYWORD_MODIFIER_SIGN);
    InsertKeyword("unsigned",  KEYWORD_MODIFIER_SIGN);
    InsertKeyword("inline",    KEYWORD_OTHERS);
    InsertKeyword("sizeof",    KEYWORD_OTHERS);
    InsertKeyword("const",     KEYWORD_QUALIFIER);
    InsertKeyword("volatile",  KEYWORD_QUALIFIER);
    InsertKeyword("return",    KEYWORD_RETURN);
    InsertKeyword("extern",    KEYWORD_STORAGE_CLASS);
    InsertKeyword("register",  KEYWORD_STORAGE_CLASS);
    InsertKeyword("static",    KEYWORD_STORAGE_CLASS);
    InsertKeyword("auto",      KEYWORD_STORAGE_CLASS);
    InsertKeyword("typedef",   KEYWORD_STORAGE_CLASS);
    InsertKeyword("struct",    KEYWORD_STRUCT);
    InsertKeyword("union",     KEYWORD_UNION);

    /* additionally defined keywords */
    InsertKeyword("const&volatile", KEYWORD_QUALIFIER);
    InsertKeyword("long long",      KEYWORD_MODIFIER_LEN);

    InsertOperator("#",    TYPE_PREPROCESS);
    InsertOperator("+",    TYPE_ADDOP);
    InsertOperator("-",    TYPE_ADDOP);
    InsertOperator("->",   TYPE_ARROW);
    InsertOperator("=",    TYPE_ASSIGNOP);
    InsertOperator("+=",   TYPE_ASSIGNOP);
    InsertOperator("-=",   TYPE_ASSIGNOP);
    InsertOperator("*=",   TYPE_ASSIGNOP);
    InsertOperator("/=",   TYPE_ASSIGNOP);
    InsertOperator("%=",   TYPE_ASSIGNOP);
    InsertOperator("&=",   TYPE_ASSIGNOP);
    InsertOperator("^=",   TYPE_ASSIGNOP);
    InsertOperator("|=",   TYPE_ASSIGNOP);
    InsertOperator("~=",   TYPE_ASSIGNOP);
    InsertOperator(">>=",  TYPE_ASSIGNOP);
    InsertOperator("<<=",  TYPE_ASSIGNOP);
    InsertOperator("&",    TYPE_BIT_AND);
    InsertOperator("~",    TYPE_BIT_NOT);
    InsertOperator("|",    TYPE_BIT_OR);
    InsertOperator("^",    TYPE_BIT_XOR);
    InsertOperator(":",    TYPE_COLON);
    InsertOperator(",",    TYPE_COMMA);
    InsertOperator("//",   TYPE_COMMENT_LINE);
    InsertOperator("/*",   TYPE_COMMENT_START);
    InsertOperator("*/",   TYPE_COMMENT_START);
    InsertOperator("?",    TYPE_CONDITIONOP_1);
    InsertOperator(":",    TYPE_CONDITIONOP_2);
    InsertOperator(".",    TYPE_DOT);
    InsertOperator("==",   TYPE_EQUAL_COMPOP);
    InsertOperator("!",    TYPE_EQUAL_COMPOP);
    InsertOperator("++",   TYPE_INCOP);
    InsertOperator("--",   TYPE_INCOP);
    InsertOperator(">",    TYPE_INEQUAL_COMPOP);
    InsertOperator("<",    TYPE_INEQUAL_COMPOP);
    InsertOperator(">=",   TYPE_INEQUAL_COMPOP);
    InsertOperator("<=",   TYPE_INEQUAL_COMPOP);
    InsertOperator("{",    TYPE_LEFT_BRACE);
    InsertOperator("[",    TYPE_LEFT_BRACKET);
    InsertOperator("(",    TYPE_LEFT_PARENTHESIS);
    InsertOperator("&&",   TYPE_LOGIC_AND);
    InsertOperator("!",    TYPE_LOGIC_NOT);
    InsertOperator("||",   TYPE_LOGIC_OR);
    InsertOperator("*",    TYPE_MULOP);
    InsertOperator("/",    TYPE_MULOP);
    InsertOperator("%",    TYPE_MULOP);
    InsertOperator("}",    TYPE_RIGHT_BRACE);
    InsertOperator("]",    TYPE_RIGHT_BRACKET);
    InsertOperator(")",    TYPE_RIGHT_PARENTHESIS);
    InsertOperator(";",    TYPE_SEMICOLON);
    InsertOperator(">>",   TYPE_SHIFTOP);
    InsertOperator("<<",   TYPE_SHIFTOP);
    InsertOperator("\\",   TYPE_BACKSLASH);

    return;
}

/* for debug use only*/
void PrintAll()
{
    int i;
    for (i = 0; i <HASH_TABLE_SIZE; i++)
    {
        Symbol_T *tmp = HashTable[i];
        while (tmp)
        {
            if (tmp->Type == TYPE_IDENTIFIER)
                printf("%x\t%s\t%s\t%s\t%s\t%s\t%s\n", i, tmp->Name, tmp->StorageClass->Name, tmp->ModifierOfLen->Name, tmp->ModifierOfSign->Name, tmp->Qualifier->Name, tmp->DataType->Name);
            tmp = tmp->Next;
        }
    }
    return;
}
